use proc_macro::TokenStream;
use quote::quote;
use syn::{
    parse::Parse, parse::ParseStream, parse_macro_input, DeriveInput, Lit, Meta, MetaNameValue,
    Token,
};

// 定义用于解析GVK参数的结构体
struct GvkArgs {
    metas: Vec<Meta>,
}

impl Parse for GvkArgs {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let mut metas = Vec::new();
        while !input.is_empty() {
            metas.push(input.parse()?);
            if input.peek(Token![,]) {
                input.parse::<Token![,]>()?;
            }
        }
        Ok(GvkArgs { metas })
    }
}

/// GVK宏，用于自动为结构体添加Extension相关的字段和实现
///
/// 使用方式：
/// ```
/// use macros::gvk;
///
/// #[gvk(group = "core.halo.run", version = "v1alpha1", kind = "User", singular = "user", plural = "users")]
/// struct User {
///     spec: UserSpec,
///     status: UserStatus,
/// }
/// ```
#[proc_macro_attribute]
pub fn gvk(args: TokenStream, input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let args = parse_macro_input!(args as GvkArgs);

    // 解析GVK参数
    let mut group = String::new();
    let mut version = String::new();
    let mut kind = String::new();
    let mut singular = String::new();
    let mut plural = String::new();

    for arg in args.metas {
        if let Meta::NameValue(MetaNameValue { path, value, .. }) = arg {
            let name = path.get_ident().unwrap().to_string();
            if let syn::Expr::Lit(syn::ExprLit {
                                      lit: Lit::Str(lit_str),
                                      ..
                                  }) = value
            {
                match name.as_str() {
                    "group" => group = lit_str.value(),
                    "version" => version = lit_str.value(),
                    "kind" => kind = lit_str.value(),
                    "singular" => singular = lit_str.value(),
                    "plural" => plural = lit_str.value(),
                    _ => {}
                }
            }
        }
    }

    let struct_name = &input.ident;
    let vis = &input.vis;

    // 获取原始结构体的字段
    let original_fields = if let syn::Data::Struct(data_struct) = &input.data {
        &data_struct.fields
    } else {
        panic!("gvk宏只能用于结构体");
    };

    // 提取字段名称用于new()方法
    let field_names: Vec<_> = original_fields
        .iter()
        .filter_map(|f| f.ident.as_ref())
        .collect();

    // 为每个原始字段添加serde默认值属性
    let fields_with_attrs: Vec<_> = original_fields
        .iter()
        .map(|field| {
            let field_name = &field.ident;
            let field_type = &field.ty;
            let field_vis = &field.vis;
            quote! {
                #[serde(default)]
                #field_vis #field_name: #field_type
            }
        })
        .collect();

    // 生成API版本字符串
    let api_version = if group.is_empty() {
        version.clone()
    } else {
        format!("{}/{}", group, version)
    };

    let expanded = quote! {
        #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
        #vis struct #struct_name {
            #[serde(rename = "apiVersion")]
            api_version: String,
            kind: String,
            metadata: halo_model::Metadata,
            #(#fields_with_attrs,)*
        }

        impl #struct_name {
            pub const GROUP: &'static str = #group;
            pub const VERSION: &'static str = #version;
            pub const KIND: &'static str = #kind;
            pub const SINGULAR: &'static str = #singular;
            pub const PLURAL: &'static str = #plural;

            pub fn new() -> Self {
                Self {
                    api_version: #api_version.to_string(),
                    kind: #kind.to_string(),
                    metadata: halo_model::Metadata::default(),
                    #(#field_names: Default::default(),)*
                }
            }
        }

        impl Default for #struct_name {
            fn default() -> Self {
                Self::new()
            }
        }

        impl halo_model::GVK for #struct_name {
            fn group() -> &'static str {
                Self::GROUP
            }

            fn version() -> &'static str{
                Self::VERSION
            }

            fn kind() -> &'static str{
                Self::KIND
            }

            fn plural() -> &'static str{
                Self::SINGULAR
            }

            fn singular() -> &'static str{
                Self::PLURAL
            }


        }

        impl halo_model::ExtensionOperator for #struct_name {
            fn get_api_version(&self) -> &str {
                &self.api_version
            }

            fn get_kind(&self) -> &str {
                &self.kind
            }

            fn get_metadata(&self) -> &halo_model::Metadata {
                &self.metadata
            }

            fn get_metadata_mut(&mut self) -> &mut halo_model::Metadata {
                &mut self.metadata
            }

            fn set_api_version(&mut self, api_version: String) {
                self.api_version = api_version;
            }

            fn set_kind(&mut self, kind: String) {
                self.kind = kind;
            }

            fn set_metadata(&mut self, metadata: halo_model::Metadata) {
                self.metadata = metadata;
            }

            fn group_version_kind(&self) -> halo_model::GroupVersionKind {
                halo_model::GroupVersionKind::new(#group.to_string(), #version.to_string(), #kind.to_string())
            }

            fn set_group_version_kind(&mut self, gvk: halo_model::GroupVersionKind) {
                self.api_version = gvk.api_version();
                self.kind = gvk.kind().to_string();
            }
        }

        impl halo_model::Extension for #struct_name {}

        impl PartialEq for #struct_name {
            fn eq(&self, other: &Self) -> bool {
                self.metadata.name == other.metadata.name
            }
        }

        impl Eq for #struct_name {}

        impl PartialOrd for #struct_name {
            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
                Some(self.cmp(other))
            }
        }

        impl Ord for #struct_name {
            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
                self.metadata.name.cmp(&other.metadata.name)
            }
        }
    };

    TokenStream::from(expanded)
}
